﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Web.Mvc;
using Nop.Core;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Plugin.Payments.Assist.Models;
using Nop.Services.Configuration;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Orders;
using Nop.Services.Payments;
using Nop.Web.Framework;
using Nop.Web.Framework.Controllers;

namespace Nop.Plugin.Payments.Assist.Controllers
{
    public class PaymentAssistController : BaseNopPaymentController
    {
        private readonly ISettingService _settingService;
		private readonly ILocalizationService _localizationService;
		private readonly IPaymentService _paymentService;
        private readonly IOrderService _orderService;
        private readonly IOrderProcessingService _orderProcessingService;
		private readonly ILogger _logger;
		private readonly IWebHelper _webHelper;
        private readonly AssistPaymentSettings _assistPaymentSettings;
        private readonly PaymentSettings _paymentSettings;
        private readonly IWorkContext _workContext;

		public PaymentAssistController(ISettingService settingService,
			ILocalizationService localizationService, 
            IPaymentService paymentService, IOrderService orderService,
			IOrderProcessingService orderProcessingService,
			ILogger logger, IWebHelper webHelper,
            AssistPaymentSettings assistPaymentSettings,
            PaymentSettings paymentSettings, IWorkContext workContext)
        {
            this._settingService = settingService;
			this._localizationService = localizationService;
			this._paymentService = paymentService;
            this._orderService = orderService;
            this._orderProcessingService = orderProcessingService;
			this._logger = logger;
			this._webHelper = webHelper;
            this._assistPaymentSettings = assistPaymentSettings;
            this._paymentSettings = paymentSettings;
            this._workContext = workContext;
        }
        
        [AdminAuthorize]
        [ChildActionOnly]
        public ActionResult Configure()
        {
            var model = new ConfigurationModel();
			model.UseSandbox = _assistPaymentSettings.UseSandbox;
			model.GatewayUrl = _assistPaymentSettings.GatewayUrl;
            model.MerchantId = _assistPaymentSettings.MerchantId;
			model.UserName = _assistPaymentSettings.UserName;
			model.Password = _assistPaymentSettings.Password;
			model.TransactModeId = Convert.ToInt32(_assistPaymentSettings.TransactMode);
			model.AdditionalFee = _assistPaymentSettings.AdditionalFee;
			model.TransactModeValues = _assistPaymentSettings.TransactMode.ToSelectList();
           
            return View("Nop.Plugin.Payments.Assist.Views.PaymentAssist.Configure", model);
        }

        [HttpPost]
        [AdminAuthorize]
        [ChildActionOnly]
        public ActionResult Configure(ConfigurationModel model)
        {
            if (!ModelState.IsValid)
                return Configure();

            //save settings
			_assistPaymentSettings.UseSandbox = model.UseSandbox;
			_assistPaymentSettings.GatewayUrl = model.GatewayUrl;
            _assistPaymentSettings.MerchantId = model.MerchantId;
			_assistPaymentSettings.UserName = model.UserName;
			_assistPaymentSettings.Password = model.Password;
			_assistPaymentSettings.TransactMode = (TransactMode)model.TransactModeId;
            _assistPaymentSettings.AdditionalFee = model.AdditionalFee;
            _settingService.SaveSetting(_assistPaymentSettings);

			model.TransactModeValues = _assistPaymentSettings.TransactMode.ToSelectList();

            return View("Nop.Plugin.Payments.Assist.Views.PaymentAssist.Configure", model);
        }

        [ChildActionOnly]
        public ActionResult PaymentInfo()
        {
            var model = new PaymentInfoModel();
            return View("Nop.Plugin.Payments.Assist.Views.PaymentAssist.PaymentInfo", model);
        }

        [NonAction]
        public override IList<string> ValidatePaymentForm(FormCollection form)
        {
            var warnings = new List<string>();
            return warnings;
        }

        [NonAction]
        public override ProcessPaymentRequest GetPaymentInfo(FormCollection form)
        {
            var paymentInfo = new ProcessPaymentRequest();
            return paymentInfo;
        }

        [ValidateInput(false)]
        public ActionResult Return(FormCollection form)
        {
			string assistOrderNumber = _webHelper.QueryString<string>("ordernumber");

			var processor = _paymentService.LoadPaymentMethodBySystemName("Payments.Assist") as AssistPaymentProcessor;
            if (processor == null ||
                !processor.IsPaymentMethodActive(_paymentSettings) || !processor.PluginDescriptor.Installed)
                throw new NopException("Assist module cannot be loaded");

			var order = _orderService.GetOrderById(int.Parse(assistOrderNumber));

			if (order == null || order.Deleted)
			{
				return new HttpUnauthorizedResult();
			}
			else
			{
				Dictionary<string, string> response;
				PaymentStatus orderStatus = order.PaymentStatus;
				if (orderStatus == PaymentStatus.Authorized || orderStatus == PaymentStatus.Paid)
				{
					return RedirectToAction("Index", "Home", new { area = "" });
				}
				else
				{
					if (processor.GetOrderState(assistOrderNumber, out response, out orderStatus))
					{
						decimal amount = decimal.Zero;
						try
						{
							amount = decimal.Parse(response["orderamount"], new CultureInfo("en-US"));
						}
						catch (Exception exc)
						{
							_logger.Error("Assist. Error getting order amount. " + orderStatus, exc);
						}

						string billnumber = string.Empty;
						response.TryGetValue("billnumber", out billnumber);
						string orderstate = string.Empty;
						response.TryGetValue("orderstate", out orderstate);
						string ordercurrency = string.Empty;
						response.TryGetValue("ordercurrency", out ordercurrency);
						string packetdate = string.Empty;
						response.TryGetValue("packetdate", out packetdate);
						string signature = string.Empty;
						response.TryGetValue("signature", out signature);
						string checkvalue = string.Empty;
						response.TryGetValue("checkvalue", out checkvalue);

						var sb = new StringBuilder();
						sb.AppendLine("Assist payment result:");
						sb.AppendLine("Order state: " + orderstate);
						sb.AppendLine("Amount: " + amount);
						sb.AppendLine("Currency: " + ordercurrency);
						sb.AppendLine("Packet date: " + packetdate);
						sb.AppendLine("Signature: " + signature);
						sb.AppendLine("Check value: " + checkvalue);

						//order note
						order.OrderNotes.Add(new OrderNote()
						{
							Note = sb.ToString(),
							DisplayToCustomer = false,
							CreatedOnUtc = DateTime.UtcNow
						});
						_orderService.UpdateOrder(order);

						//validate order total
						if (_assistPaymentSettings.ValidateOrderTotal && !Math.Round(amount, 2).Equals(Math.Round(order.OrderTotal, 2)))
						{
							string errorStr = string.Format("Assist. Returned order amount {0} doesn't equal order amount {1}", amount, order.OrderTotal);
							_logger.Error(errorStr);

							return RedirectToAction("Index", "Home", new { area = "" });
						}

						//mark order as authorized or paid
						order.AuthorizationTransactionId = billnumber;
						order.AuthorizationTransactionResult = orderstate;

						switch (orderStatus)
						{
							case PaymentStatus.Authorized:
								if (_orderProcessingService.CanMarkOrderAsAuthorized(order))
									_orderProcessingService.MarkAsAuthorized(order);
								break;
							case PaymentStatus.Paid:
								order.CaptureTransactionId = billnumber;
								order.CaptureTransactionResult = orderstate;

								if (_orderProcessingService.CanMarkOrderAsPaid(order))
									_orderProcessingService.MarkOrderAsPaid(order);
								break;
							default:
								break;
						}
					}
					else
					{
						//order note
						order.OrderNotes.Add(new OrderNote()
						{
							Note = "Payment failed. Assist Error: " + AssistHelper.GetError(response),
							DisplayToCustomer = false,
							CreatedOnUtc = DateTime.UtcNow
						});
						_orderService.UpdateOrder(order);

						return RedirectToAction("Index", "Home", new { area = "" });
					}
				}
			}

			return RedirectToRoute("CheckoutCompleted");
		}
    }
}